﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace Casamiel.Infrastructure
{
    /// <summary>
    /// 
    /// </summary>
    public static class DictionaryUtil
    {
        public static void Add<T>(Dictionary<string, string> dic, string key, T value)
        {
            if (null == value)
            {
                return;
            }
            if (dic == null)
            {
                dic = new Dictionary<string, string>();
            }
            else if (dic.ContainsKey(key))
            {
                dic.Remove(key);
            }
            dic.Add(key, value.ToString());
        }

        public static string Get(Dictionary<string, string> dic, string key)
        {
			if (dic is null) {
				throw new ArgumentNullException(nameof(dic));
			}

			if (dic.ContainsKey(key))
            {
                return dic[key];
            }
            return null;
        }

        public static string Pop(Dictionary<string, string> dic, string key)
        {
			if (dic is null) {
				throw new ArgumentNullException(nameof(dic));
			}

			string value = null;
            if (dic.ContainsKey(key))
            {
                value = dic[key];
                dic.Remove(key);
            }
            return value;
        }
    }
    /// <summary>
    /// 
    /// </summary>
    public class JsonReader  
    {

        private static Object ARRAY_END_TOKEN = new Object();
        private static Object OBJECT_END_TOKEN = new Object();
        private static Object COMMA_TOKEN = new Object();
        private static Object COLON_TOKEN = new Object();

        private const int FIRST_POSITION = 0;
        private const int CURRENT_POSITION = 1;
        private const int NEXT_POSITION = 2;

        private CharEnumerator ct;
        private char c;

        private Object token;
        private StringBuilder sb = new StringBuilder();
        private Dictionary<String, String> map = new Dictionary<String, String>();

        private static Dictionary<char, char> escapes = new Dictionary<char, char> {
            {'\\','\\'},
            {'/', '/'},
            {'"', '"'},
            {'t','\t'},
            {'n','\n'},
            {'r','\r'},
            {'b','\b'},
            {'f','\f'}
        };

        public Dictionary<String, String> Read(String response, String endpoint)
        {
			if (string.IsNullOrEmpty(response)) {
				throw new ArgumentException("message", nameof(response));
			}

			return Read(response.GetEnumerator(), endpoint);
        }

        public Dictionary<String, String> Read(CharEnumerator ci, String endpoint)
        {
            ct = ci;
            NextChar();
            ReadJson(endpoint);
            return map;
        }

        private Object ReadJson(String baseKey)
        {
            SkipWhiteSpace();
            char ch = c;
            NextChar();
            switch (ch)
            {
                case '{': ProcessObject(baseKey); break;
                case '}': token = OBJECT_END_TOKEN; break;
                case '[':
                    if (c == '"')
                    {
                        ProcessList(baseKey); break;
                    }
                    else
                    {
                        ProcessArray(baseKey); break;
                    }
                case ']': token = ARRAY_END_TOKEN; break;
                case '"': token = ProcessString(); break;
                case ',': token = COMMA_TOKEN; break;
                case ':': token = COLON_TOKEN; break;
                case 't':
                    NextChar(); NextChar(); NextChar();
                    token = true;
                    break;
                case 'n':
                    NextChar(); NextChar(); NextChar();
                    token = null;
                    break;
                case 'f':
                    NextChar(); NextChar(); NextChar(); NextChar();
                    token = false;
                    break;
                default:
                    //c = ct.previous();
                    if (Char.IsDigit(ch) || ch == '-')
                    {
                        token = ProcessNumber(ch);
                    }
                    break;
            }
            return token;
        }

        private void ProcessObject(String baseKey)
        {
            String key = baseKey + "." + ReadJson(baseKey);
            while (token != OBJECT_END_TOKEN)
            {
                ReadJson(key);
                if (token != OBJECT_END_TOKEN)
                {
                    object obj = ReadJson(key);

                    if (obj is String || obj is Boolean || obj is Double)
                    {
                        DictionaryUtil.Add(map, key, obj.ToString());
                    }

                    if (ReadJson(key) == COMMA_TOKEN)
                    {
                        key = ReadJson(key).ToString();
                        key = baseKey + "." + key;
                    }
                }
            }
        }

        private void ProcessList(String baseKey)
        {
            Object value = ReadJson(baseKey);
            int index = 0;
            while (token != ARRAY_END_TOKEN)
            {
                String key = TrimFromLast(baseKey, ".") + "[" + (index++) + "]";
                DictionaryUtil.Add(map, key, value.ToString());
                if (ReadJson(baseKey) == COMMA_TOKEN)
                {
                    value = ReadJson(baseKey);
                }
            }
            DictionaryUtil.Add(map, TrimFromLast(baseKey, ".") + ".Length", index.ToString());
        }

        private void ProcessArray(String baseKey)
        {
            int index = 0;
            String preKey = baseKey.Substring(0, baseKey.LastIndexOf("."));
            String key = preKey + "[" + index + "]";
            Object value = ReadJson(key);

            while (token != ARRAY_END_TOKEN)
            {
                DictionaryUtil.Add(map, preKey + ".Length", (index + 1).ToString());
                if (value is String)
                {
                    DictionaryUtil.Add(map, key, value.ToString());
                }
                if (ReadJson(baseKey) == COMMA_TOKEN)
                {
                    key = preKey + "[" + (++index) + "]";
                    value = ReadJson(key);
                }
            }
        }

        private Object ProcessNumber(char preChar)
        {
            sb.Clear();
            sb.Append(preChar);
            if ('-' == c)
            {
                AddChar();
            }
            AddDigits();
            if ('.' == c)
            {
                AddChar();
                AddDigits();
            }
            if ('e' == c || 'E' == c)
            {
                AddChar();
                if ('+' == c || '-' == c)
                {
                    AddChar();
                }
                AddDigits();
            }
            return sb.ToString();
        }

        private void AddDigits()
        {
            while (Char.IsDigit(c))
            {
                AddChar();
            }
        }

        private void SkipWhiteSpace()
        {
            while (Char.IsWhiteSpace(c))
            {
                NextChar();
            }
        }

        private char? NextChar()
        {
            if (ct.MoveNext())
            {
                c = ct.Current;
                return c;
            }
            return null;
        }

        private Object ProcessString()
        {
            sb.Clear();
            while (c != '"')
            {
                if (c == '\\')
                {
                    NextChar();
                    Object value = escapes[c];
                    if (value != null)
                    {
                        AddChar((Char)value);
                    }
                }
                else
                {
                    AddChar();
                }
            }
            NextChar();
            return sb.ToString();
        }

        private void AddChar(char ch)
        {
            sb.Append(ch);
            NextChar();
        }

        private void AddChar()
        {
            AddChar(c);
        }

        public static String TrimFromLast(String str, String stripString)
        {
            int pos = str.LastIndexOf(stripString);
            if (pos > -1)
            {
                return str.Substring(0, pos);
            }
            else
            {
                return str;
            }
        }

    }
    public class XmlReader
    {
        Dictionary<String, String> dictionary = new Dictionary<String, String>();

        public Dictionary<String, String> Read(String xml, String endpoint)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);
            Read(doc.ChildNodes[1], endpoint, false);
            return dictionary;
        }

        private void Read(XmlNode element, String path, bool appendPath)
        {
            path = appendPath ? path + "." + element.Name : path;
            if (element.InnerText.Equals(element.InnerXml,StringComparison.InvariantCultureIgnoreCase))
            {
                dictionary.Add(path, element.InnerText);
                return;
            }
            XmlNodeList listElements = element.SelectNodes(element.FirstChild.Name);
            if (listElements.Count > 1 && element.ChildNodes.Count == listElements.Count)
            {//be list
                ElementsAsList(element.ChildNodes, path);
            }
            else if (listElements.Count == 1 && element.ChildNodes.Count == 1)
            {//may be list
                ElementsAsList(listElements, path);//as list
                Read(element.FirstChild, path, true);//as not list
            }
            else
            {//not list
                foreach (XmlNode childElement in element.ChildNodes)
                {
                    Read(childElement, path, true);
                }
            }
        }

        private void ElementsAsList(XmlNodeList listElements, String path)
        {
            dictionary.Add(path + ".Length", listElements.Count.ToString());
            for (int i = 0; i < listElements.Count; i++)
            {
                Read(listElements[i], path + "[" + i + "]", false);
            }
        }

    }
    /// <summary>
    /// 
    /// </summary>
    public class SendSmsResponse
    {

        private string requestId;

        private string bizId;

        private string code;

        private string message;

        public string RequestId
        {
            get
            {
                return requestId;
            }
            set
            {
                requestId = value;
            }
        }

        public string BizId
        {
            get
            {
                return bizId;
            }
            set
            {
                bizId = value;
            }
        }

        public string Code
        {
            get
            {
                return code;
            }
            set
            {
                code = value;
            }
        }

        public string Message
        {
            get
            {
                return message;
            }
            set
            {
                message = value;
            }
        }
    }

    /// <summary>
    /// 阿里云短信发送
    /// </summary>
    public static class SmsHelper
    {

        private static string BuildQuery(IDictionary<string, string> parameters)
        {
            if (parameters == null || parameters.Count == 0)
            {
                return null;
            }
            StringBuilder query = new StringBuilder();
            bool hasParam = false;
            foreach (KeyValuePair<string, string> kv in parameters)
            {
                string name = kv.Key; string value = kv.Value; // 忽略参数名或参数值为空的参数 
                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
                {
                    if (hasParam)
                    {
                        query.Append("&");
                    }
                    query.Append(name);
                    query.Append("=");
                    query.Append(PercentEncode(value));
                    hasParam = true;
                }
            }
            return query.ToString();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="secret"></param>
        /// <param name="signMethod"></param>
        /// <returns></returns>
        private static string GetAlidayuSign(IDictionary<string, string> parameters, string secret, string signMethod)
        { //把字典按Key的字母顺序排序 
            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal); //把所有参数名和参数值串在一起 
            StringBuilder query = new StringBuilder();
            //if (Constants.SIGN_METHOD_MD5.Equals(signMethod))
            //{ query.Append(secret);
            //}
            foreach (KeyValuePair<string, string> kv in sortedParams)
            {
                if (!string.IsNullOrEmpty(kv.Key) && !string.IsNullOrEmpty(kv.Value))
                {
                    query.Append("&").Append(kv.Key).Append("=").Append(PercentEncode(kv.Value));
                }
            } //使用MD5/HMAC加密 
            if (signMethod == "HMAC-SHA1")
            {
                var sortstring = query.ToString().Substring(1);
                sortstring = "GET&" + PercentEncode("/") + "&" + PercentEncode(sortstring);
                return Hmac(sortstring, secret);
            }
            else
            {
                query.Append(secret);
                return Md5(query.ToString());
            }
        }
        private static string Hmac(string value, string key)
        {
            key += "&";
            var hashAlgorithm = new HMACSHA1();
            //hashAlgorithm.HashName = "HMACSHA1";
            hashAlgorithm.Key = Encoding.UTF8.GetBytes(key.ToCharArray());
            return Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(value.ToCharArray())));
        }

        private static string PercentEncode(String value)
        {
            StringBuilder stringBuilder = new StringBuilder();
            string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
            byte[] bytes = Encoding.GetEncoding("UTF-8").GetBytes(value);
            foreach (char c in bytes)
            {
                if (text.IndexOf(c) >= 0)
                {
                    stringBuilder.Append(c);
                }
                else
                {
                    stringBuilder.Append("%").Append(
                        string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c));
                }
            }
            return stringBuilder.ToString();
        }
        private static string Md5(string value)
        {
            byte[] bytes;
            using (var md5 = MD5.Create())
            {
                bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(value));
            }
            var result = new StringBuilder();
            foreach (byte t in bytes)
            {
                result.Append(t.ToString("X2"));
            }
            return result.ToString();
        }
        /// <summary>
        /// 发送短信
        /// </summary>
        /// <param name="appKey"></param>
        /// <param name="appSecret"></param>
        /// <param name="mobile">手机号</param>
        /// <param name="signName">短信签名</param>
        /// <param name="templateParam">内容json</param>
        /// <param name="templateCode">模板id</param>
        /// <param name="outId">outId</param>
        /// <returns></returns>
        public static async Task<SendSmsResponse> SendSms(string appKey,string appSecret,string mobile, string signName, string templateParam, string templateCode, string outId)
        {
           // string appKey = "LTAIT6Qwbp0PdpJE";// "LTAIijSYyNUFQMkb";
            //string appSecret = "HX1uQDI29BKYmQOvNTh9fTSRYUQy5L";// "OMmdKg00DJKTOZCsOBXTqfbfF4Uqp9";
            var txtParams = new SortedDictionary<string, string>();
            txtParams.Add("SignatureNonce", Guid.NewGuid().ToString());
            txtParams.Add("SignatureVersion", "1.0");
            txtParams.Add("SignatureMethod", "HMAC-SHA1");
            txtParams.Add("AccessKeyId", appKey);
            txtParams.Add("Format", "JSON");
            txtParams.Add("Timestamp", DateTime.Now.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.CreateSpecificCulture("en-US")));
            txtParams.Add("Action", "SendSms");
            txtParams.Add("Version", "2017-05-25");
            txtParams.Add("RegionId", "cn-hangzhou");
            txtParams.Add("PhoneNumbers", mobile);
            txtParams.Add("SignName", signName);
            txtParams.Add("TemplateParam", templateParam);
            txtParams.Add("TemplateCode", templateCode);
            txtParams.Add("OutId", outId);

            IOrderedEnumerable<string> enumerable = from p in txtParams.Keys
                                                    orderby p
                                                    select p;
            Dictionary<string, string> dictionary = new Dictionary<string, string>();
            foreach (string item in enumerable)
            {
                dictionary.Add(item, txtParams[item]);
            }
            dictionary.Add("Signature", GetAlidayuSign(dictionary, appSecret, "HMAC-SHA1"));

            try
            {
                var url = "http://dysmsapi.aliyuncs.com/?";
				using (HttpClient _client = new HttpClient())
				{
					using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, url + BuildQuery(dictionary)))
					{
						using (var response = await _client.SendAsync(requestMessage).ConfigureAwait(false))
						{
							var a = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
							var reader = new JsonReader();
							var dic = reader.Read(a, "SendSmsResponse");

							var sendSmsResponse = new SendSmsResponse
							{
								RequestId = DictionaryStringValue("SendSmsResponse.RequestId", dic),
								BizId = DictionaryStringValue("SendSmsResponse.BizId", dic),
								Code = DictionaryStringValue("SendSmsResponse.Code", dic),
								Message = DictionaryStringValue("SendSmsResponse.Message", dic)
							};

							return sendSmsResponse;
						}


					}
				}
			}
            catch
            {
                throw;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dic"></param>
        /// <returns></returns>
        private static string DictionaryStringValue(string key, Dictionary<string, string> dic)
        {
            if (null != DictionaryGet(dic, key))
            {
                return DictionaryGet(dic, key);
            }
            return null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private static string DictionaryGet(Dictionary<string, string> dic, string key)
        {
            if (dic.ContainsKey(key))
            {
                return dic[key];
            }
            return null;
        }
    }
}